package name.elftesting.vnctest;

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import javax.imageio.ImageIO;

import name.elftesting.tess4j.Tesseract;
import name.elftesting.tess4j.TesseractException;
import name.elftesting.vncelf.Constant;
import name.elftesting.vncelf.JSplashWindowEx;
import name.elftesting.vncelf.KeyMap;
import name.elftesting.vncelf.LoginFrame;
import name.elftesting.vncelf.VncViewer;

import org.sikuli.script.*;

/**
 * 
 * @author Xiaoping Wu
 * 
 */
public class VncElf {

	public static void main(String args[]) {
		args = new String[] { "host", "10.231.52.21", "port", "5900","password","firewall"};
	
			
		VncElf vr = new VncElf();
		vr.init(args);
		//vr.mouseClick(600, 800, 1000);
		//vr.mousePress(500, 100, 300);
//		for(int i=0;i<300;i++){
//		vr.pressKey("BACKSPACE", 300);
//		System.out.println(1000);
//		}
		//vr.destroy();
	}
	
	public final static int ByProperties=0;
	public final static int ByRecoginzed=1;
	private VncViewer viewer;
	private Tesseract ocr;

	/**
	 * init VNC viewer
	 * 
	 * @param args
	 */

	public void init(String args[]) {
		VncViewer v = new VncViewer();
		v.mainArgs = args;
		v.inAnApplet = false;
		v.inSeparateFrame = true;


		v.init();
		v.start();
		while (Constant.isRun == false) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.viewer = v;
	}

	public void init(VncViewer v) {
		this.viewer = v;
	}

	/**
	 * Exit VNC, Close all the connection
	 */
	public void destroy() {

		viewer.disconnect();

	}

	/**
	 * Set status bar
	 * 
	 * @param text
	 */
	public void setStatusBar(String text) {
		viewer.statusBar.setText(text);
	}
	
	/**
	 * 
	 * @param String
	 */
	public void log(Object str) {
		str = str + "\n";
		viewer.logTextArea.append(str.toString());
	}
	/**
	 * 
	 * @param string
	 * @param b true in a line. false second line. 
	 */
	public void log(Object str, boolean b) {
		if (b == true)
			str = str + "\n";
		viewer.logTextArea.append(str.toString());
	}
	
	/**
	 * Get remote copied text
	 * @return
	 */
	public String getClipboardText() {
		return viewer.clipboard.getCutText();
	}
	
	/**
	 * program sleep for some times
	 * @param waitTime
	 */
	public void waitSleep(int waitTime) {
		try {
			Thread.sleep(waitTime);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Mouse move action to X,Y
	 * 
	 * @param x
	 * @param y
	 * @param waitTime
	 */
	public void mouseMoved(int x, int y, int waitTime) {
		MouseEvent me = new MouseEvent(viewer.vc, 0, 0, 0, x, y, 0, true, 0);
		viewer.vc.processLocalMouseEvent(me, true);
		this.waitSleep(waitTime);

	}

	/**
	 * Mouse press action. Doesn't release
	 * 
	 * @param x
	 * @param y
	 * @param waitTime
	 */
	public void mousePress(int x, int y, int waitTime) {
		//MouseEvent me = new MouseEvent(viewer.vc, 0, 0, 0, x, y, 0, true, 0);
		MouseEvent me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_PRESSED, 0,
				0, x, y, 1, true, 1);
		viewer.vc.processLocalMouseEvent(me, false);
		this.waitSleep(waitTime);


	}
	
	/**
	 * Mouse press action. release the mouse
	 * 
	 * @param x
	 * @param y
	 * @param waitTime
	 */
	public void mouseRelease(int x, int y, int waitTime) {
		//MouseEvent me = new MouseEvent(viewer.vc, 0, 0, 0, x, y, 0, true, 0);
		MouseEvent me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_RELEASED, 0,
				0, x, y, 1, true, 1);
		viewer.vc.processLocalMouseEvent(me, false);
		this.waitSleep(waitTime);

	}

	/**
	 * Mouse click action. Press than release
	 * 
	 * @param x
	 * @param y
	 * @param waitTime
	 */
	public void mouseClick(int x, int y, int waitTime) {
		MouseEvent me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_PRESSED, 0,
				0, x, y, 1, true, 1);
		viewer.vc.processLocalMouseEvent(me, false);
	
		me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_RELEASED, 0, 0, x, y,
				1, true, 1);
		viewer.vc.processLocalMouseEvent(me, false);
		this.waitSleep(waitTime);
	}

	/**
	 * Right mouse key clicked
	 * 
	 * @param x
	 * @param y
	 * @param waitTime
	 */

	public void mouseRightClick(int x, int y, int waitTime) {
		MouseEvent me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_PRESSED, 0,
				0, x, y, 1, true, 3);
		viewer.vc.processLocalMouseEvent(me, false);
		me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_RELEASED, 0, 0, x, y,
				1, true, 3);
		viewer.vc.processLocalMouseEvent(me, false);

		this.waitSleep(waitTime);
	}
/**
 * Middle mouse key clicked
 * @param x
 * @param y
 * @param waitTime
 */
	public void mouseMiddleClick(int x, int y, int waitTime) {
		MouseEvent me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_PRESSED, 0,
				0, x, y, 1, true, 2);
		viewer.vc.processLocalMouseEvent(me, false);
		me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_RELEASED, 0, 0, x, y,
				1, true, 2);
		viewer.vc.processLocalMouseEvent(me, false);

		this.waitSleep(waitTime);
	}
	/**
	 * Mouse drag action from point to new point
	 * 
	 * @param fromX
	 * @param fromY
	 * @param toX
	 * @param toY
	 * @param waitTime
	 */
	public void mouseDrag(int fromX, int fromY, int toX, int toY, int waitTime) {
		MouseEvent me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_PRESSED, 0,
				0, fromX, fromY, 1, true, 1);
		viewer.vc.processLocalMouseEvent(me, false);

		me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_DRAGGED, 0, 0, toX,
				toY, 0, true, 1);
		viewer.vc.processLocalMouseEvent(me, true);
		this.waitSleep(500);
		me = new MouseEvent(viewer.vc, MouseEvent.MOUSE_RELEASED, 0, 0, toX,
				toY, 1, true, 1);
		viewer.vc.processLocalMouseEvent(me, false);
		this.waitSleep(waitTime);
	}

	/**
	 * Type String
	 * 
	 * @param str
	 * @param sWaitTime 
	 * @param waitTime
	 */
	public void type(String str,int sWaitTime, int waitTime) {
		String input_temp = str.toUpperCase();
		for (int j = 0; j < str.length(); j++) {
			char c = str.charAt(j);
			char c_temp = input_temp.charAt(j);
			KeyEvent exe = new KeyEvent(viewer.vc, KeyEvent.KEY_PRESSED, 0, 0,
					(int) c_temp, c, KeyEvent.KEY_LOCATION_STANDARD);

			viewer.vc.processLocalKeyEvent(exe);
			this.waitSleep(sWaitTime);
			viewer.vc.keyTyped(exe);
			exe = new KeyEvent(viewer.vc, KeyEvent.KEY_RELEASED, 0, 0,
					(int) c_temp, c, KeyEvent.KEY_LOCATION_STANDARD);
			viewer.vc.processLocalKeyEvent(exe);

		}
		this.waitSleep(waitTime);
	}
	/**
	 * Type String
	 * 
	 * @param str
	 * @param waitTime
	 */
	public void type(String str, int waitTime) {
		this.type(str,100, waitTime);
	}

	/**
	 * Press a key
	 * 
	 * @param keyName
	 * @param waitTime
	 */
	public void pressKey(String keyName, int waitTime) {
		KeyEvent exe = new KeyEvent(viewer.vc, KeyEvent.KEY_PRESSED, 0, 0,
				KeyMap.getKeyValue(keyName));
		viewer.vc.processLocalKeyEvent(exe);
		this.waitSleep(100);
		viewer.vc.keyTyped(exe);
		exe = new KeyEvent(viewer.vc, KeyEvent.KEY_RELEASED, 0, 0,
				KeyMap.getKeyValue(keyName));
		viewer.vc.processLocalKeyEvent(exe);
		this.waitSleep(waitTime);
	}

	/**
	 * Capture whole screen. return BufferedImage. Using ImageIO.write to output
	 * a file
	 * 
	 * @return
	 */
	private BufferedImage captureScreen() {
		int width = viewer.vc.getWidth();
		int height = viewer.vc.getHeight();
		BufferedImage fullScreen = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g = fullScreen.createGraphics();
		viewer.canvasPanel.paint(g);
		return fullScreen;
	}

	public void captureScreen(String path) {
		BufferedImage bi = this.captureScreen();
		try {
			ImageIO.write(bi, "png", new File(path));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Click screen by image. the image is you saved before. By properties
	 * @param image
	 * @param waitTime
	 */
	public void clickImage(String image, int waitTime) {
		this.clickImage(image, 0, waitTime);
	}
	/**
	 * Click screen by image. the image is you saved before. 0 properties; 1, by recognized.
	 * @param image
	 * @param c
	 * @param waitTime
	 */
	public void clickImage(String image, int c, int waitTime) {
		Rectangle r = null;
		if (c == VncElf.ByProperties)
			r = this.finderImageByProperties(image);
		else if (c == VncElf.ByRecoginzed) {
			try {
				r = this.finderImageByRecognize(image);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			this.log("the image find way is error  input 1 or 0");
		}
		this.mouseClick(r.x + 2, r.y + 2, waitTime);

	}

	/**
	 * Compare the screen and the picture. if the screen has the image return true. else return false.
	 * @param image
	 * @return
	 */
	public boolean compareScreen(String image) {
		Rectangle r = null;
		try {
			r = this.finderImageByRecognize(image);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (r == null)
			return false;
		else
			return true;
	}
	
	/**
	 * Find the image from screen return int[]. int[0] x, int[1] y, int [2] width, int [3] height;
	 * @param image
	 * @return
	 */
	public int[] findScreen(String image) {
		int[] i = new int[4];
		Rectangle r = null;
		try {
			r = this.finderImageByRecognize(image);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (r != null) {
			i[0] = r.x;
			i[1] = r.y;
			i[2] = r.width;
			i[3] = r.height;
			return i;
		} else {
			return null;
		}

	}
	/**
	 * Wait for image display on the screen. total wait time is timeout*cycles. Doesn't display return null.
	 * @param image
	 * @param timeout the wait time of every cycle
	 * @param cycles the cycles program wait.
	 * @return
	 */
	public boolean wait4Screen(String image, int timeout, int cycles) {
		boolean find = false;
		int c = 0;
		while (find == false && c < cycles) {
			Rectangle r = null;
			try {
				r = this.finderImageByRecognize(image);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (r != null) {
				find = true;
				break;
			}
			this.waitSleep(timeout);
			c++;
		}
		return false;
	}

	/**
	 * Compare two image files
	 * 
	 * @param sourceFilePath
	 * @param targetFilePath
	 * @return
	 * @throws IOException
	 */

	public double compareImage(String sourceFilePath, String targetFilePath)
			throws IOException {
		Finder finder = new Finder(sourceFilePath);
		org.sikuli.script.Pattern p = new org.sikuli.script.Pattern(
				targetFilePath);
		finder.find(p);
		org.sikuli.script.Match m = finder.next();
		if (m != null)
			return m.getScore();
		else
			return 0;
	}

	/**
	 * Compare two buffered image
	 * 
	 * @param sourceImage
	 * @param targetImage
	 * @return
	 * @throws Exception
	 */
	private double compareImage(BufferedImage sourceImage,
			BufferedImage targetImage) throws Exception {
		File temp = new File("temp");
		if (temp.isDirectory() == false)
			temp.mkdir();
		String sourceImage1 = System.currentTimeMillis() + "1.png";
		String targetImage1 = System.currentTimeMillis() + "2.png";
		File source = new File(temp.getAbsolutePath() + File.separator
				+ sourceImage1);
		File target = new File(temp.getAbsolutePath() + File.separator
				+ targetImage1);
		ImageIO.write(sourceImage, "png", source);
		ImageIO.write(targetImage, "png", target);
		Thread.sleep(1000);
		Finder finder = new Finder(source.getAbsolutePath());
		org.sikuli.script.Pattern p = new org.sikuli.script.Pattern(
				target.getAbsolutePath());
		finder.find(p);
		org.sikuli.script.Match m = finder.next();
		source.delete();
		target.delete();
		if (m != null)
			return m.getScore();
		else
			return 0;
	}

	/**
	 * compare an image file with the screen region. Return score. if score=0,
	 * The image is different.
	 * 
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 * @param imageFilePath
	 * @return
	 * @throws IOException
	 */
	private double compareImage(int x, int y, int width, int height,
			String imageFilePath) throws IOException {
		int swidth = viewer.vc.getWidth();
		int sheight = viewer.vc.getHeight();

		BufferedImage fullScreen = new BufferedImage(swidth, sheight,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g = fullScreen.createGraphics();
		viewer.canvasPanel.paint(g);

		Rectangle r = new Rectangle(x, y, width, height);
		ScreenImage si = new ScreenImage(r, fullScreen);

		Region region = new Region(si.x, si.y, si.w, si.h);
		Finder finder = new Finder(si, region);

		org.sikuli.script.Pattern p = new org.sikuli.script.Pattern(
				imageFilePath);
		finder.find(p);
		org.sikuli.script.Match m = finder.next();

		if (m != null)
			return m.getScore();
		else
			return 0;

	}

	private static Properties loadFile(String path) {
		Properties prop = null;
		try {
			BufferedInputStream inBuff = new BufferedInputStream(
					new FileInputStream(path));
			prop = new Properties();
			prop.load(inBuff);
			inBuff.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return prop;
	}

	/**
	 * Get the image point and Rectangle by impage properties which is stored
	 * when capture screen
	 * 
	 * @param imageFilePath
	 * @return
	 */
	private Rectangle finderImageByProperties(String imageFilePath) {
		Properties prop = loadFile(imageFilePath + ".properties");
		Rectangle r = null;
		if (prop != null) {
			int x = Integer.valueOf((String) prop.get("x"));
			int y = Integer.valueOf((String) prop.get("y"));
			int width = Integer.valueOf((String) prop.get("width"));
			int height = Integer.valueOf((String) prop.get("height"));
			r = new Rectangle(x, y, width, height);

		}
		return r;
	}

	/**
	 * Get the image by image recognize. If rectangle=null, couldn't find the
	 * image
	 * 
	 * @param imageFilePath
	 * @return
	 * @throws IOException
	 */
	private Rectangle finderImageByRecognize(String imageFilePath)
			throws IOException {
		int swidth = viewer.vc.getWidth();
		int sheight = viewer.vc.getHeight();

		BufferedImage fullScreen = new BufferedImage(swidth, sheight,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g = fullScreen.createGraphics();
		viewer.canvasPanel.paint(g);

		Rectangle r = new Rectangle(0, 0, swidth, sheight);
		ScreenImage si = new ScreenImage(r, fullScreen);

		Region region = new Region(si.x, si.y, si.w, si.h);
		Finder finder = new Finder(si, region);

		org.sikuli.script.Pattern p = new org.sikuli.script.Pattern(
				imageFilePath);
		finder.find(p);
		org.sikuli.script.Match m = finder.next();
		
		if (m != null)
			return new Rectangle(m.x, m.y, m.w, m.h);
		else
			return null;

	}

	/**
	 * capture screen region
	 * 
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 * @return
	 */
	public BufferedImage captureScreen(int x, int y, int width, int height) {
		BufferedImage fullScreen = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g = fullScreen.createGraphics();
		viewer.canvasPanel.paint(g);
		BufferedImage bi = fullScreen.getSubimage(x, y, width, height);
		return bi;
	}
	
	public String doOCR(File imageFile){
		ocr=Tesseract.getInstance();
		String text=null;
		try {
			text=ocr.doOCR(imageFile);
		} catch (TesseractException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return text;
	}
	
	  public String doOCR(BufferedImage bi)  {
		ocr=Tesseract.getInstance();
		String text=null;
		
		  try {
			text= ocr.doOCR(bi, null);
		} catch (TesseractException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		  return text; 
	    }


}
